package besta.moon.sql.expression.function.system;

import besta.moon.sql.Expression;
import besta.moon.sql.datatype.MutableNumeric;
import besta.moon.sql.parser.SQLTokenizer;
import besta.moon.sql.expression.Expressions;
import besta.moon.sql.expression.ExpressionArithmetic;

public final class FunctionCase extends Expression {

    public FunctionCase() {
        super(FUNCTION);
    }

    private final Expressions cases = new Expressions();
    private final Expressions results = new Expressions();
    private Expression elseResult = Expression.NULL;
    private int dataType = -1;

    public final void addCase(Expression condition, Expression result) {
        cases.add(condition);
        results.add(result);
    }

    public final void setElseResult(Expression expr) {
        elseResult = expr;
    }

    public final void setEnd() {
        Expression[] params = new Expression[cases.size() * 2
                + (elseResult != null ? 1 : 0)];
        int i = 0;
        for (int p = 0; p < cases.size(); p++) {
            params[i++] = cases.get(p);
            params[i++] = results.get(p);
        }
        if (i < params.length) {
            params[i] = elseResult;
        }
        super.setParams(params);
    }

    @Override
    public final void setParams(Expression[] params) {
        super.setParams(params);
        int i = 0;
        for (int p = 0; p < cases.size(); p++) {
            cases.set(p, params[i++]);
            results.set(p, params[i++]);
        }
        if (i < params.length) {
            elseResult = params[i];
        }
    }

    @Override
    public void setParamAt(Expression param, int idx) {
        super.setParamAt(param, idx);
        int p = idx / 2;
        if (p >= cases.size()) {
            elseResult = param;
            return;
        }
        if (idx % 2 > 0) {
            results.set(p, param);
        } else {
            cases.set(p, param);
        }
    }

    final int getFunction() {
        return SQLTokenizer.CASE;
    }

    @Override
    public final boolean isNull() throws Exception {
        return getResult().isNull();
    }

    @Override
    public final boolean getBoolean() throws Exception {
        return getResult().getBoolean();
    }

    @Override
    public final int getInt() throws Exception {
        return getResult().getInt();
    }

    @Override
    public final long getLong() throws Exception {
        return getResult().getLong();
    }

    @Override
    public final float getFloat() throws Exception {
        return getResult().getFloat();
    }

    @Override
    public final double getDouble() throws Exception {
        return getResult().getDouble();
    }

    @Override
    public final long getMoney() throws Exception {
        return getResult().getMoney();
    }

    @Override
    public final MutableNumeric getNumeric() throws Exception {
        return getResult().getNumeric();
    }

    @Override
    public final Object getObject() throws Exception {
        return getResult().getObject();
    }

    @Override
    public final String getString() throws Exception {
        return getResult().getString();
    }

    @Override
    public final byte[] getBytes() throws Exception {
        return getResult().getBytes();
    }

    @Override
    public final int getDataType() {
        if (dataType < 0) {
            dataType = elseResult.getDataType();
            for (int i = 0; i < results.size(); i++) {
                dataType = ExpressionArithmetic.getDataType(dataType, results
                        .get(i).getDataType());
            }
        }
        return dataType;
    }

    @Override
    public final int getPrecision() {
        int precision = 0;
        for (int i = results.size() - 1; i >= 0; i--) {
            precision = Math.max(precision, results.get(i).getPrecision());
        }
        return precision;
    }

    @Override
    public final int getScale() {
        int precision = 0;
        for (int i = results.size() - 1; i >= 0; i--) {
            precision = Math.max(precision, results.get(i).getScale());
        }
        return precision;
    }

    private Expression getResult() throws Exception {
        for (int i = 0; i < cases.size(); i++) {
            if (cases.get(i).getBoolean()) {
                return results.get(i);
            }
        }
        return elseResult;
    }

}
